/*
 * txdnet.cn tonydon
 * by netbeans ide 7.2
 */
package cn.txdnet.txweb.basedao.mybatis;

import cn.txdnet.txweb.basedao.AbstractSqlSessionMgr;
import cn.txdnet.txweb.basedao.TransIsoLevel;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionException;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import txdnet.util.DataUtil;

/**
 *
 * @author tangxiaodong
 */
public class MybatisSqlSessionMgr extends AbstractSqlSessionMgr<SqlSession> {

    /**
     * 默认 Mybatis配置文件
     */
    public static final String DEFAULT_CONFIG_FILE = "mybatis.cfg.xml";
    public static final String DEFAULT_ENV = "jndi-env";
    /**
     * 当前Mybatis配置环境
     */
    private String env = DEFAULT_ENV;
    /**
     * Mybatis Sql Session Factory 工厂对象
     */
    private SqlSessionFactory sessionFactory;
    /**
     * Sql session 对象 auto commit 属性
     */
    private final ThreadLocal<Boolean> localAutoCommit = new ThreadLocal<Boolean>();
    /**
     * Sql Session 对象 executor type 属性
     */
    private final ThreadLocal<ExecutorType> localExecutorType = new ThreadLocal<ExecutorType>();

    @Override
    public void init(String... args) {
        if (args.length == 0) {
            initialize();
        } else if (args.length == 1) {
            initialize(args[0]);
        } else if (args.length == 2) {
            initialize(args[0], args[1]);
        } else {
            throw new InvalidParameterException("MyBatisSqlSessionMgr Initialize Fail (Invalid Parameters)");
        }
    }

    /**
     * 使用默认的 MyBatis 配置文件及其默认配置环境配置 Session Factory
     *
     * @throws SqlSessionException	: 配置失败时抛出该异常
     */
    public void initialize() {
        initialize(DEFAULT_CONFIG_FILE, DEFAULT_ENV);
    }

    /**
     * 使用特定的 MyBatis 配置文件及其默认配置环配置 Session Factory
     *
     * @param mybatisCfgFile	: 配置文件路径
     * @throws SqlSessionException	: 配置失败时抛出该异常
     */
    public void initialize(String mybatisCfgFile) {
        initialize(mybatisCfgFile, DEFAULT_ENV);
    }

    /**
     * 使用特定的 MyBatis 配置文件和特定配置环境配置 Session Factory
     *
     * @param mybatisCfgFile	: 配置文件路径
     * @param env	: 配置环境名称
     * @throws SqlSessionException	: 配置失败时抛出该异常
     */
    public void initialize(String mybatisCfgFile, String environment) {
        configFile = DataUtil.checkNull(mybatisCfgFile, DEFAULT_CONFIG_FILE);
        env = DataUtil.checkNull(environment, DEFAULT_ENV);
        try {
            loadDefaultTransIsoLevel();
        } catch (SqlSessionException e) {
            destroy();
            throw e;
        }
    }

    @Override
    public void destroy() {
        sessionFactory = null;
    }

    @Override
    public void beginTrans() {
        getSession(false);
    }

    @Override
    public void commit() {
        commit(true);
    }

    /**
     * 提交事务
     *
     * @param force
     */
    public final void commit(boolean force) {
        SqlSession sn = localSession.get();
        if (sn != null) {
            sn.commit(force);
        }
    }

    @Override
    public void rollback() {
        rollback(true);
    }

    /**
     * 回滚事务
     *
     * @param force
     */
    public final void rollback(boolean force) {
        SqlSession sn = localSession.get();
        if (sn != null) {
            sn.rollback(force);
        }
    }

    @Override
    public void closeSession() {
        SqlSession session = localSession.get();
        localSession.set(null);
        localAutoCommit.set(null);
        localExecutorType.set(null);
        if (session != null) {
            session.close();
        }
    }

    @Override
    public final SqlSession getSession() {
        return getSession(true);
    }

    @Override
    public void setSessionTransIsoLevel(TransIsoLevel level) {
        try {
            getSession().getConnection().setTransactionIsolation(level.intValue());
        } catch (SQLException e) {
            throw new SqlSessionException(e);
        }
    }

    @Override
    protected void loadDefaultTransIsoLevel() {
        try {
            SqlSession session = getSession();
            Connection conn = session.getConnection();
            int level = conn.getTransactionIsolation();
            defaultTransIsoLevel = TransIsoLevel.getLevel(level);
        } catch (SQLException e) {
            throw new SqlSessionException(e);
        } finally {
            closeSession();
        }
    }

    /**
     *
     * 获取绑定到当前线程的 {@link SqlSession} 实例。 如果该实例已存在则返回当前实例，否则创建一个 autoCommit 的
     * {@link SqlSession}。
     *
     * @throws SqlSessionException	: 获取 Session 实例失败时抛出该异常
     *
     */
    public final SqlSession getSession(boolean autoCommit) {
        return getSession(null, autoCommit);
    }

    /**
     * 获取绑定到当前线程的 SqlSession 实例。 如果该实例已存在则返回当前实例，否则创建一个 type autoCommit 类型 的
     * SqlSession
     *
     * @throws SqlSessionException : 获取 Session 实例失败时抛出该异常
     */
    public final SqlSession getSession(ExecutorType type, boolean autoCommit) {
        SqlSession session = localSession.get();
        if (session != null) {
            return session;
        }
        if (sessionFactory == null) {
            buildSessionFactory();
        }
        if (type == null) {
            type = localExecutorType.get();

            if (type == null) {
                type = getDefaultExecutorType();
            }
        }
        session = sessionFactory.openSession(type, autoCommit);
        localSession.set(session);
        localAutoCommit.set(autoCommit);
        localExecutorType.set(type);
        return session;
    }

    /**
     * 创建 SqlSessionFactory
     */
    private void buildSessionFactory() {
        synchronized (this) {
            if (sessionFactory == null) {
                try {
                    sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(configFile), env);
                } catch (IOException e) {
                    throw new SqlSessionException(e);
                }
            }
        }
    }

    /**
     * 把当前 SqlSession 的 ExecutorType 设置为 type
     *
     * @param type
     */
    public void changeSessionExecutorType(ExecutorType type) {
        SqlSession session = localSession.get();

        if (session == null) {
            localExecutorType.set(type);
        } else {
            if (type == null) {
                type = getDefaultExecutorType();
            }
            ExecutorType currentType = localExecutorType.get();
            if (type != currentType) {
                SqlSession newSession = sessionFactory.openSession(type, session.getConnection());
                session.clearCache();
                localSession.set(newSession);
                localExecutorType.set(type);
            }
        }
    }

    /**
     * 获取sqlsession Factory的 Configuration对象
     *
     * @return Configuration
     */
    public final Configuration getConfiguration() {
        return sessionFactory.getConfiguration();
    }

    /**
     * 获取SqlsessionFactory的默认执行类型 ExecutorType
     *
     * @return ExecutorType
     */
    public final ExecutorType getDefaultExecutorType() {
        return getConfiguration().getDefaultExecutorType();
    }

    /**
     * 获取当前sqlsession 连接
     *
     * @return
     */
    public final Connection getConnection() {
        Connection conn = null;
        SqlSession sn = localSession.get();
        if (sn != null) {
            conn = sn.getConnection();
        }
        return conn;
    }
}
